import Mermaid from '@components/Mermaid';
import { Badge } from '@theme';
import ModuleType from '../../types/module.mdx';
import ChunkType from '../../types/chunk.mdx';
import RuntimeModuleType from '../../types/runtime-module.mdx';
import SourceType from '../../types/source.mdx';
import HashType from '../../types/hash.mdx';
import CompilerType from '../../types/compiler.mdx';
import { Collapse, CollapsePanel } from '@components/Collapse';
import Columns from '@components/Columns';
import { NoSSR } from '@rspress/core/runtime';
import { ApiMeta } from '@components/ApiMeta';

# Compilation 钩子

Compilation 钩子是 Rspack 插件的主要扩展方式，为开发者提供了介入构建流程各阶段的能力。

本文档列举了 Rspack 中可用的 Compilation 钩子、它们的触发时机、参数以及使用示例。

:::tip
查看 [Compilation](/api/javascript-api/compilation) 了解更多关于 Compilation 对象的信息。
:::

:::info
Rspack 的主要编译逻辑运行在 Rust 侧。出于稳定性、性能、架构等因素，在使用钩子时，Rust 侧编译对象传输到 JavaScript 侧后，对 JavaScript 侧的修改不会被同步到 Rust 侧。因此绝大部分钩子为“只读”。
:::

## Overview

<NoSSR>

<Columns styles={[{}, { flex: "0.8" }]}>

<Mermaid title="compilation.addEntry()">

{`
flowchart TD
EntryPlugin("EntryPlugin") --> AddEntry("compilation.addEntry(callback)")
AddEntry --> HookAddEntry(hooks.addEntry)
subgraph BuildModuleGraph["创建模块图"]
HookAddEntry --> FactorizeModule("ModuleFactory.create()")
FactorizeModule <--> SideEffectsResolve(<a href="/zh/config/optimization#optimizationsideeffects">检测模块副作用</a>)
FactorizeModule <--> Resolve("解析模块路径")

FactorizeModule --> BuildModule("compilation.buildModule()")
BuildModule --> HookStillValidModule{hooks.stillValidModule}
HookStillValidModule --> |true| ProcessDependencies("处理模块依赖")
HookStillValidModule --> |false| HookBuildModule(<a href="#buildmodule">hooks.buildModule</a>)
HookBuildModule --> ModuleBuild("module.build()")
ModuleBuild --> RunLoaders("运行 Loader 编译模块")
RunLoaders --> ParserParse("解析模块依赖")
ParserParse --> ModuleBuild
ParserParse <--> InnerGraph(<a href="/zh/config/optimization#optimizationinnergraph">分析变量引用</a>)
ParserParse <--> SideEffectsCode(<a href="/zh/config/optimization#optimizationsideeffects">检测代码副作用</a>)
ModuleBuild --> |success| HookSucceedModule(<a href="#succeedmodule">hooks.succeedModule</a>)
ModuleBuild --> |failed| HookFailedModule(hooks.failedModule)
HookSucceedModule --> ProcessDependencies
HookFailedModule --> ProcessDependencies
ProcessDependencies --> FactorizeModule
ProcessDependencies --> |failed| HookFailedEntry(hooks.failedEntry)
ProcessDependencies --> |success| HookSucceedEntry(hooks.succeedEntry)
HookFailedEntry --> Callback("callback()")
HookSucceedEntry --> Callback

end

class AddEntry flow-start
class Callback flow-end
class FactorizeModule,Resolve,BuildModule,ProcessDependencies,RunLoaders,ParserParse,ModuleBuild flow-process
class InnerGraph,SideEffectsResolve,SideEffectsCode flow-optimization
class HookBuildModule,HookSucceedModule flow-hook
class HookStillValidModule,HookAddEntry,HookFailedEntry,HookSucceedEntry,HookFailedModule flow-hook-non-support
`}

</Mermaid>

<div>
<Mermaid title="compilation.finish()">

{`
flowchart TD
CompilerCompile("compiler.compile()") --> CompilationFinish("compilation.finish(callback)")
CompilationFinish --> HookFinishModules(<a href="#finishmodules">hooks.finishModules</a>)
HookFinishModules <--> FlagDependencyExports(<a href="/zh/config/optimization#optimizationprovidedexports">分析模块导出</a>)
HookFinishModules --> Callback("callback()")

class CompilationFinish flow-start
class Callback flow-end
class FlagDependencyExports flow-optimization
class HookFinishModules flow-hook
`}

</Mermaid>

<br />

<Mermaid title="Stats">

{`
flowchart TD
StatsToString("stats.toString()") --> CreateStatsOptions("compilation.createStatsOptions")
CreateStatsOptions --> HookStatsPreset(<a href="#statspreset">hooks.statsPreset</a>)
HookStatsPreset --> HookStatsNormalize(<a href="#statsnormalize">hooks.statsNormalize</a>)
HookStatsNormalize --> CreateStatsOptions
CreateStatsOptions --> HookStatsFactory(<a href="#statsfactory">hooks.statsFactory</a>)
HookStatsFactory --> HookStatsPrinter(<a href="#statsprinter">hooks.statsPrinter</a>)
HookStatsPrinter --> StatsJSON(<a href="/zh/api/javascript-api/stats-json">生成 Stats 对象</a>)
StatsJSON --> StatsOutput("生成 Stats 字符串")

class StatsToString flow-start
class StatsOutput flow-end
class CreateStatsOptions,StatsJSON flow-process
class HookStatsFactory,HookStatsPrinter,HookStatsPreset,HookStatsNormalize flow-hook
`}

</Mermaid>

</div>

</Columns>

<br />

<Mermaid title="compilation.seal()">

{`
flowchart TD
subgraph Start
direction LR
CompilerCompile("compiler.compile()") --> Seal("compilation.seal(callback)")
Seal --> HookSeal(<a href="#seal">hooks.seal</a>)

class HookSeal flow-hook
end

Start --> ChunkGraph

subgraph ChunkGraph["创建 Chunk 图"]
direction LR

    subgraph OptimizeDependencies["优化模块图"]
    direction TB
    HooksOptimizationDependencies(hooks.optimizeDependencies) <--> FlagUsedExports(<a href="/zh/config/optimization#optimizationusedexports">分析被使用的导出</a>)
    HooksOptimizationDependencies --> HookAfterOptimizeDependencies(hooks.afterOptimizeDependencies)

    class HooksOptimizationDependencies,HookAfterOptimizeDependencies flow-hook-non-support
    class FlagUsedExports flow-optimization
    end

    OptimizeDependencies --> GenerateChunkGraph

    subgraph GenerateChunkGraph["生成 Chunk 图"]
    direction TB
    HookBeforeChunks(hooks.beforeChunks) --> CreateEntryChunks("创建入口 Chunk")
    CreateEntryChunks --> BuildChunkGraph("从入口出发生成 Chunk 图")
    BuildChunkGraph --> HookAfterChunks(hooks.afterChunks)

    class HookBeforeChunks,HookAfterChunks flow-hook-non-support
    class CreateEntryChunks,BuildChunkGraph flow-process
    class FlagUsedExports flow-optimization
    end

end

ChunkGraph --> Optimization

subgraph Optimization["优化模块和 Chunk"]
direction LR

    subgraph OptimizeModules["优化模块"]
    direction TB
    HookOptimize(hooks.optimize) --> HookOptimizeModules(<a href="#optimizemodules">hooks.optimizeModules</a>)
    HookOptimizeModules --> HookAfterOptimizeModules(<a href="#afteroptimizemodules">hooks.afterOptimizeModules</a>)

    class HookOptimize flow-hook-non-support
    class HookOptimizeModules,HookAfterOptimizeModules flow-hook-partial-support
    end

    OptimizeModules --> OptimizeChunks["优化 Chunk"]

    subgraph OptimizeChunks
    HookOptimizeChunks(hooks.optimizeChunks) <--> SplitChunks(<a href="/zh/config/optimization#optimizationsplitchunks">拆分 Chunk 优化</a>)
    HookOptimizeChunks --> HookAfterOptimizeChunks(hooks.afterOptimizeChunks)

    class HookOptimizeChunks,HookAfterOptimizeChunks flow-hook-non-support
    class SplitChunks flow-optimization
    end

    OptimizeChunks --> OptimizeTree

    subgraph OptimizeTree["优化 Chunk 组"]
    HookOptimizeTree(<a href="#optimizetree">hooks.optimizeTree</a>) --> HookAfterOptimizeTree(hooks.afterOptimizeTree)

    class HookOptimizeTree flow-hook-partial-support
    class HookAfterOptimizeTree flow-hook-non-support
    end

    OptimizeTree --> OptimizeChunkModules

    subgraph OptimizeChunkModules["优化 Chunk 内的模块"]
    HookOptimizeChunkModules(<a href="#optimizechunkmodules">hooks.optimizeChunkModules</a>) <--> ModuleConcatenation(<a href="/zh/config/optimization#optimizationconcatenatemodules">合并拼接模块</a>)
    HookOptimizeChunkModules --> HookAfterOptimizeChunkModules(hooks.afterOptimizeChunkModules)
    HookAfterOptimizeChunkModules --> HookShouldRecord(hooks.shouldRecord)

    class HookOptimizeChunkModules flow-hook-partial-support
    class HookShouldRecord,HookAfterOptimizeChunkModules flow-hook-non-support
    class ModuleConcatenation flow-optimization
    end

end

Optimization --> GenerateIds

subgraph GenerateIds["生成 ID 信息"]
direction LR

    subgraph CreateModuleIds["生成模块 ID"]
    HookReviveModules(hooks.reviveModules) --> HookBeforeModuleIds(hooks.beforeModuleIds)
    HookBeforeModuleIds --> HookModuleIds(hooks.moduleIds)
    HookModuleIds --> HookOptimizeModuleIds(hooks.optimizeModuleIds)
    HookOptimizeModuleIds --> HookAfterOptimizeModuleIds(hooks.afterOptimizeModuleIds)

    class HookReviveModules,HookModuleIds,HookBeforeModuleIds,HookOptimizeModuleIds,HookAfterOptimizeModuleIds flow-hook-non-support
    end

    CreateModuleIds --> CreateChunkIds

    subgraph CreateChunkIds["生成 Chunk ID"]
    HookReviveChunks(hooks.reviveChunks) --> HookBeforeChunkIds(hooks.beforeChunkIds)
    HookBeforeChunkIds --> HookChunkIds(hooks.moduleIds)
    HookChunkIds --> HookOptimizeChunkIds(hooks.optimizeChunkIds)
    HookOptimizeChunkIds --> HookAfterOptimizeChunkIds(hooks.afterOptimizeChunkIds)

    class HookReviveChunks,HookChunkIds,HookBeforeChunkIds,HookOptimizeChunkIds,HookAfterOptimizeChunkIds flow-hook-non-support
    end

    CreateChunkIds --> CreateRecords

    subgraph CreateRecords["生成编译记录"]
    ShouldRecord{"shouldRecord"} --> |true| HookRecordModules(hooks.recordModules)
    ShouldRecord{"shouldRecord"} --> |false| HookOptimizeCodeGeneration(hooks.optimizeCodeGeneration)
    HookRecordModules --> HookRecordChunks(hooks.recordChunks)
    HookRecordChunks --> HookOptimizeCodeGeneration(hooks.optimizeCodeOptions)

    class ShouldRecord,HookRecordModules,HookRecordChunks,HookOptimizeCodeGeneration flow-hook-non-support
    class SplitChunks flow-optimization
    end

end

GenerateIds --> CodeGeneration

subgraph CodeGeneration["生成模块和 Chunk 代码"]
direction LR

    subgraph CreateModuleHashes["生成模块哈希"]
    HookBeforeModuleHash(hooks.beforeModuleHash) --> GenerateModuleHashes("生成模块哈希")
    GenerateModuleHashes --> HookAfterModuleHash(hooks.afterModuleHash)

    class HookBeforeModuleHash,HookAfterModuleHash flow-hook-non-support
    class GenerateModuleHashes flow-process
    end

    CreateModuleHashes --> ModuleGeneration

    subgraph ModuleGeneration["生成模块代码"]
    HookBeforeModuleCodeGeneration(hooks.beforeModuleCodeGeneration) --> ModuleCodeGeneration("生成模块代码")
    ModuleCodeGeneration --> HookAfterModuleCodeGeneration(hooks.afterModuleCodeGeneration)

    class HookBeforeModuleCodeGeneration,HookAfterModuleCodeGeneration flow-hook-non-support
    class ModuleCodeGeneration flow-process
    end

    ModuleGeneration --> CollectRuntimeRequirements

    subgraph CollectRuntimeRequirements["收集运行时模块依赖"]
    HookBeforeRuntime(hooks.beforeRuntimeRequirements) --> HookModuleRuntime(hooks.runtimeRequirementInModule)
    HookModuleRuntime --> HookAdditionalChunkRuntime(hooks.additionalChunkRuntimeRequirements)
    HookAdditionalChunkRuntime --> HookChunkRuntime(hooks.runtimeRequirementInChunk)
    HookChunkRuntime --> HookAdditionalTreeRuntime(<a href="#additionaltreeruntimerequirements">hooks.additionalTreeRuntimeRequirements</a>)
    HookAdditionalTreeRuntime --> HookTreeRuntime(<a href="#runtimerequirementintree">hooks.runtimeRequirementInTree</a>)
    HookTreeRuntime --> HookAfterRuntimeRequirements(hooks.afterRuntimeRequirements)
    HookTreeRuntime <--> HookRuntimeModule(<a href="#runtimemodule">hooks.runtimeModule</a>)

    class HookBeforeRuntime,HookModuleRuntime,HookAdditionalChunkRuntime,HookChunkRuntime,HookAfterRuntimeRequirements, flow-hook-non-support
    class HookAdditionalTreeRuntime,HookRuntimeModule,HookTreeRuntime flow-hook-partial-support
    class ModuleCodeGeneration flow-process
    end

    CollectRuntimeRequirements --> CompilationHash

    subgraph CompilationHash["生成 Compilation 哈希"]
    HookBeforeHash(hooks.beforeHash) --> CreateHash("生成 Compilation 哈希")
    CreateHash --> HookChunkHash(<a href="#chunkhash">hooks.chunkHash</a>)
    HookChunkHash --> HookFullHash(hooks.fullHash)
    HookFullHash --> CreateHash
    CreateHash --> HookAfterHash(hooks.afterHash)

    class HookBeforeHash,HookAfterHash,HookFullHash flow-hook-non-support
    class HookChunkHash flow-hook-partial-support
    class CreateHash flow-process
    end

end

CodeGeneration --> Assets

subgraph Assets["生成产物信息"]
direction LR
subgraph CreateModuleAssets["生成模块产物"]
ShouldRecord2{"shouldRecord"} --> |true| HookRecordHash(hooks.recordHash)
HookRecordHash --> HookBeforeModuleAssets(hooks.beforeModuleAssets)
ShouldRecord2{"shouldRecord"} --> |false| HookBeforeModuleAssets
HookBeforeModuleAssets --> GenerateModuleAssets("生成模块产物")
GenerateModuleAssets <--> HookModuleAsset(hooks.moduleAsset)

    class ShouldRecord2,HookRecordHash,HookBeforeModuleAssets,HookModuleAsset flow-hook-non-support
    class GenerateModuleAssets flow-process
    end

    CreateModuleAssets --> CreateChunkAssets

    subgraph CreateChunkAssets["生成 Chunk 产物"]
    HookShouldGenerateChunkAssets{hooks.shouldGenerateChunkAssets} --> |true| HookBeforeChunkAssets(hooks.beforeChunkAssets)
    HookBeforeChunkAssets --> GenerateChunkAssets("生成 Chunk 产物")
    GenerateChunkAssets --> HookRenderManifest(hooks.renderManifest)
    HookRenderManifest --> HookChunkAsset(<a href="#chunkasset">hooks.chunkAsset</a>)
    HookChunkAsset --> GenerateChunkAssets
    HookShouldGenerateChunkAssets --> |false| HookProcessAssets(<a href="#processassets">hooks.processAssets</a>)

    class HookBeforeChunkAssets,HookShouldGenerateChunkAssets,HookRenderManifest flow-hook-non-support
    class HookChunkAsset flow-hook-partial-support
    class HookProcessAssets flow-hook
    class GenerateChunkAssets flow-process
    end

    CreateChunkAssets --> ProcessAssets

    subgraph ProcessAssets["加工产物"]
      HookProcessAssets2(<a href="#processassets">hooks.processAssets</a>) --> HookAfterProcessAssets(<a href="#afterprocessassets">hooks.afterProcessAssets</a>)
      HookAfterProcessAssets --> |shouldRecord=true| HookRecord(hooks.record)

      class HookProcessAssets2,HookAfterProcessAssets flow-hook
      class HookRecord flow-hook-non-support

    end

end

Assets --> End

subgraph End
direction LR
HookNeedAdditionalSeal --> |true| HookUnseal(hooks.unseal)
HookNeedAdditionalSeal --> |false| HookAfterSeal
HookUnseal --> Reseal("compilation.seal()")
HookAfterSeal(<a href="#afterseal">hooks.afterSeal</a>) --> Callback("callback()")

class HookAfterSeal flow-hook
class HookNeedAdditionalSeal,HookUnseal flow-hook-non-support
class Reseal flow-start
end

End -. additional seal .-> Start

class Seal flow-start
class Callback flow-end

`}

</Mermaid>

</NoSSR>

## `buildModule`

<Badge text="只读" type="info" />

在模块被构建之前调用。

- **类型：** `SyncHook<[Module]>`
- **参数：**
  - `Module`：模块实例

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `executeModule`

<Badge text="只读" type="info" />

若存在编译期执行模块，将在模块被执行时调用。

- **类型：** `SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>`
- **参数：**
  - `ExecuteModuleArgument`：模块运行参数
  - `ExecuteModuleContext`：模块运行上下文

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="ExecuteModuleArgument.ts" key="ExecuteModuleArgument">
```ts
type ExecuteModuleArgument = {
  codeGenerationResult: {
    get(sourceType: string): string;
  };
  moduleObject: {
    id: string;
    exports: any;
    loaded: boolean;
    error?: Error;
  };
};
```
  </CollapsePanel>
    <CollapsePanel className="collapse-code-panel" header="ExecuteModuleContext.ts" key="ExecuteModuleContext">
```ts
type ExecuteModuleContext = {
  __webpack_require__: (id: string) => any;
};
```
  </CollapsePanel>
</Collapse>

## `succeedModule`

<Badge text="只读" type="info" />

在模块成功构建后调用。

- **类型：** `SyncHook<[Module]>`
- **参数：**
  - `Module`：模块实例

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `finishModules`

<Badge text="只读" type="info" />

当所有模块都没有错误地构建完成时调用。

- **类型：** `AsyncSeriesHook<[Module[]]>`
- **参数：**
  - `Module[]`：所有模块列表

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `optimizeModules`

<Badge text="只读" type="info" />

在模块优化阶段开始时调用。

- **类型：** `SyncBailHook<[Module[]]>`
- **参数：**
  - `Module[]`：所有模块列表

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `seal`

停止接收新模块并开始优化前触发。

- **类型：** `SyncHook<[]>`

## `afterOptimizeModules`

<Badge text="只读" type="info" />

在模块优化完成之后调用。

- **类型：** `SyncBailHook<[Module[]]>`
- **参数：**
  - `Module[]`：所有模块列表

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `optimizeTree`

<Badge text="只读" type="info" />

在优化依赖树之前调用。

- **类型：** `AsyncSeriesHook<[Chunk[], Module[]]>`
- **参数：**
  - `Chunk[]`：Chunk 列表：
  - `Module[]`：模块列表

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="Chunk.ts" key="Chunk">
    <ChunkType />
  </CollapsePanel>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `optimizeChunkModules`

<Badge text="只读" type="info" />

在树优化之后，chunk 模块优化开始时调用。

- **类型：** `AsyncSeriesBailHook<[Chunk[], Module[]]>`
- **参数：**
  - `Chunk[]`：Chunk 列表：
  - `Module[]`：模块列表

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="Chunk.ts" key="Chunk">
    <ChunkType />
  </CollapsePanel>
  <CollapsePanel
    className="collapse-code-panel"
    header="Module.ts"
    key="Module"
  >
    <ModuleType />
  </CollapsePanel>
</Collapse>

## `additionalTreeRuntimeRequirements`

在树运行时依赖计算完成后调用。

- **类型：** `SyncHook<[Chunk, Set<RuntimeGlobals>]>`
- **参数：**
  - `Chunk`：Chunk 实例
  - `Set<RuntimeGlobals>`：运行时依赖集合

可通过修改运行时依赖集合以添加额外的内置运行时模块：

```js title="rspack.config.mjs"
export default {
  entry: './index.js',
  plugins: [
    {
      apply(compiler) {
        const { RuntimeGlobals } = compiler.rspack;
        compiler.hooks.thisCompilation.tap('CustomPlugin', compilation => {
          compilation.hooks.additionalTreeRuntimeRequirements.tap(
            'CustomPlugin',
            (_, set) => {
              // 添加访问 compilation 哈希值的运行时模块
              set.add(RuntimeGlobals.getFullHash);
            },
          );
        });
      },
    },
  ],
};
```

```js title="index.js"
// 将打印 compilation 的哈希值
console.log(__webpack_require__.h);
```

## `runtimeRequirementInTree`

<ApiMeta addedVersion="1.0.6" />

在根据运行时依赖以添加运行时模块时调用。

- **类型：** `HookMap<SyncBailHook<[Chunk, Set<RuntimeGlobals>]>>`
- **参数：**
  - `Chunk`：Chunk 实例
  - `Set<RuntimeGlobals>`：运行时依赖集合

可通过修改运行时依赖集合以添加额外的内置运行时模块，也可通过 [`compilation.addRuntimeModule`](/api/javascript-api/compilation#addruntimemodule) 添加自定义运行时模块：

```js title="rspack.config.mjs"
export default {
  entry: './index.js',
  plugins: [
    {
      apply(compiler) {
        const { RuntimeGlobals, RuntimeModule } = compiler.rspack;
        class CustomRuntimeModule extends RuntimeModule {
          constructor() {
            super('custom');
          }

          generate() {
            const compilation = this.compilation;
            return `
            __webpack_require__.mock = function(file) {
              return ${RuntimeGlobals.publicPath} + "/subpath/" + file;
            };
          `;
          }
        }

        compiler.hooks.thisCompilation.tap('CustomPlugin', compilation => {
          compilation.hooks.runtimeRequirementInTree
            .for(RuntimeGlobals.ensureChunkHandlers)
            .tap('CustomPlugin', (chunk, set) => {
              // 添加访问 publicPath 的运行时模块
              set.add(RuntimeGlobals.publicPath);
              // 添加自定义运行时模块
              compilation.addRuntimeModule(chunk, new CustomRuntimeModule());
            });
        });
      },
    },
  ],
};
```

```js title="index.js"
// 将打印 "/subpath/index.js"
console.log(__webpack_require__.mock('index.js'));
```

## `runtimeModule`

在运行时模块被添加后调用。

- **类型：** `SyncHook<[RuntimeModule, Chunk]>`
- **参数：**
  - `RuntimeModule`：运行时模块
  - `Chunk`：Chunk 实例

可通过 `source` 字段修改该运行时模块生成的代码。

```js title="rspack.config.mjs"
export default {
  plugins: [
    {
      apply(compiler) {
        const { RuntimeGlobals } = compiler.rspack;
        compiler.hooks.compilation.tap('CustomPlugin', compilation => {
          compilation.hooks.runtimeModule.tap(
            'CustomPlugin',
            (module, chunk) => {
              if (module.name === 'public_path' && chunk.name === 'main') {
                const originSource = module.source.source.toString('utf-8');
                module.source.source = Buffer.from(
                  `${RuntimeGlobals.publicPath} = "/override/public/path";\n`,
                  'utf-8',
                );
              }
            },
          );
        });
      },
    },
  ],
};
```

```js title="index.js"
// 将打印 "/override/public/path"
console.log(__webpack_require__.p);
```

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="RuntimeModule.ts"
    key="RuntimeModule"
  >
    <RuntimeModuleType />
  </CollapsePanel>
</Collapse>

## `processAssets`

在产物输出之前进行修改产物。

- **类型：** `AsyncSeriesHook<Assets>`
- **hook 参数：**
  - `name: string` — 插件的名称
  - `stage: Stage` — 要处理的阶段（参考 [Process assets stages](#process-assets-stages)）
- **参数：**
  - `Assets: Record<string, Source>`: 一个对象，其中 key 是 asset 的路径名，值是由 [Source](https://github.com/webpack/webpack-sources#source) 表示的 asset 数据。

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Source.ts"
    key="Source"
  >
    <SourceType />
  </CollapsePanel>
</Collapse>

### Process assets 示例

- 在 `PROCESS_ASSETS_STAGE_ADDITIONAL` 阶段输出一个新的 asset：

```js
compiler.hooks.thisCompilation.tap('MyPlugin', compilation => {
  const { Compilation } = compiler.rspack;
  compilation.hooks.processAssets.tap(
    {
      name: 'MyPlugin',
      stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
    },
    assets => {
      const { RawSource } = compiler.rspack.sources;
      const source = new RawSource('This is a new asset!');
      compilation.emitAsset('new-asset.txt', source);
    },
  );
});
```

- 更新一个已经存在的 asset：

```js
compiler.hooks.thisCompilation.tap('MyPlugin', compilation => {
  const { Compilation } = compiler.rspack;
  compilation.hooks.processAssets.tap(
    {
      name: 'MyPlugin',
      stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
    },
    assets => {
      const asset = assets['foo.js'];
      if (!asset) {
        return;
      }

      const { RawSource } = compiler.rspack.sources;
      const oldContent = asset.source();
      const newContent = oldContent + '\nconsole.log("hello world!")';
      const source = new RawSource(newContent);

      compilation.updateAsset(assetName, source);
    },
  );
});
```

- 移除一个 asset：

```js
compiler.hooks.thisCompilation.tap('MyPlugin', compilation => {
  const { Compilation } = compiler.rspack;
  compilation.hooks.processAssets.tap(
    {
      name: 'MyPlugin',
      stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
    },
    assets => {
      const assetName = 'unwanted-script.js';
      if (assets[assetName]) {
        compilation.deleteAsset(assetName);
      }
    },
  );
});
```

### Process assets stages

下面是支持的 stage 列表，Rspack 会按由上至下的顺序依次执行这些 stages，请根据你需要进行的操作来选择合适的 stage。

- `PROCESS_ASSETS_STAGE_ADDITIONAL` — 在编译中添加额外的 asset。
- `PROCESS_ASSETS_STAGE_PRE_PROCESS` — asset 进行了基础的预处理。
- `PROCESS_ASSETS_STAGE_DERIVED` — 从现有 asset 中派生新的 asset。
- `PROCESS_ASSETS_STAGE_ADDITIONS` — 为现有的 asset 添加额外的内容，例如 banner 或初始代码。
- `PROCESS_ASSETS_STAGE_OPTIMIZE` — 以通用的方式优化现有 asset。
- `PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT` — 优化现有 asset 的数量，例如，进行合并操作。
- `PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY` — 优化现有 asset 的兼容性，例如添加 polyfills 或者 vendor prefixes。
- `PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE` — 优化现有 asset 的大小，例如进行压缩或者删除空格。
- `PROCESS_ASSETS_STAGE_DEV_TOOLING` — 为 asset 添加开发者工具，例如，提取 source map。
- `PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE` — 将 asset 内联到其他 asset 中来优化现有 asset 数量。
- `PROCESS_ASSETS_STAGE_SUMMARIZE` — 整理现有 asset 列表。
- `PROCESS_ASSETS_STAGE_OPTIMIZE_HASH` — 优化 asset 的 hash 值，例如，生成基于 asset 内容的真实 hash 值。
- `PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER` — 优化已有 asset 的转换操作，例如对 asset 进行压缩，并作为独立的 asset。
- `PROCESS_ASSETS_STAGE_ANALYSE` — 分析已有 asset。
- `PROCESS_ASSETS_STAGE_REPORT` — 创建用于上报的 asset。

## `afterProcessAssets`

<Badge text="只读" type="info" />

在 [processAssets](#processAssets) hook 无错误执行后调用。

- **类型：** `SyncHook<Assets>`
- **参数：**
  - `Assets: Record<string, Source>`：产物资源映射表

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Source.ts"
    key="Source"
  >
    <SourceType />
  </CollapsePanel>
</Collapse>

- **示例：**

```js
compilation.hooks.afterProcessAssets.tap('MyPlugin', assets => {
  console.log('assets', Object.keys(assets));
});
```

## `afterSeal`

<Badge text="只读" type="info" />

在 seal 阶段结束后调用。

- **类型：** `AsyncSeriesHook<[]>`

## `chunkHash`

<Badge text="只读" type="info" />

触发来为每个 chunk 生成 hash。

- **类型：** `SyncHook<[Chunk, Hash]>`
- **参数：**
  - `Chunk`：Chunk 实例
  - `Hash`：Chunk 哈希实例

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="Chunk.ts" key="Chunk">
    <ChunkType />
  </CollapsePanel>
  <CollapsePanel className="collapse-code-panel" header="Hash.ts" key="Hash">
    <HashType />
  </CollapsePanel>
</Collapse>

## `chunkAsset`

<Badge text="只读" type="info" />

一个 chunk 中的一个 asset 被添加到 compilation 时调用。

- **类型：** `SyncHook<[Chunk, string]>`
- **参数：**
  - `Chunk`：Chunk 实例
  - `string`：产物文件名

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="Chunk.ts" key="Chunk">
    <ChunkType />
  </CollapsePanel>
</Collapse>

## `childCompiler`

<Badge text="只读" type="info" />

创建子 compiler 之后调用。

- **类型：** `SyncHook<[Compiler, string, number]>`
- **参数：**
  - `Compiler`：子编译实例：
  - `string`：子编译名称
  - `number`：子编译索引

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Compiler.ts"
    key="Compiler"
  >
    <CompilerType />
  </CollapsePanel>
</Collapse>

## `statsPreset`

<Badge text="只读" type="info" />

当使用预设 stats 配置时触发。接收一个 stats 配置对象，当插件管理 stats 预设配置时，它应当在配置对象上仔细地修改，而非直接替换整个配置对象。

- **类型：** `SyncHook<[Partial<StatsOptions>, CreateStatsOptionsContext]>`
- **参数：**
  - `Partial<StatsOptions>`：Stats 配置
  - `CreateStatsOptionsContext`：Stats 上下文

以如下插件为例：

```js
compilation.hooks.statsPreset.for('my-preset').tap('MyPlugin', options => {
  if (options.all === undefined) options.all = true;
});
```

该插件确保对于预设 `"my-preset"`，如果 `all` 选项未定义，则默认为 `true`。

<Collapse>
  <CollapsePanel header="StatsOptions.ts" key="StatsOptions">
详见 [Stats 配置](/config/stats)
  </CollapsePanel>
  <CollapsePanel className="collapse-code-panel" header="CreateStatsOptionsContext.ts" key="CreateStatsOptionsContext">
```ts
type CreateStatsOptionsContext = {
  forToString?: boolean;
  [key: string]: any;
};
```
  </CollapsePanel>
</Collapse>

## `statsNormalize`

<Badge text="只读" type="info" />

此钩子用于将选项对象转换为便于后续钩子使用的格式。它还确保缺失的选项被设置为默认值。

- **类型：** `SyncHook<[Partial<StatsOptions>, CreateStatsOptionsContext]>`
- **参数：**
  - `Partial<StatsOptions>`：Stats 配置
  - `CreateStatsOptionsContext`：Stats 上下文

以如下插件为例：

```js
compilation.hooks.statsNormalize.tap('MyPlugin', options => {
  if (options.myOption === undefined) options.myOption = [];

  if (!Array.isArray(options.myOption)) options.myOptions = [options.myOptions];
});
```

在这个插件中，如果 `myOption` 缺失，会将其设置为 `[]`。此外，它确保 `myOption` 始终是一个数组，即使它最初被定义为单个值。

<Collapse>
  <CollapsePanel header="StatsOptions.ts" key="StatsOptions">
详见 [Stats 配置](/config/stats)
  </CollapsePanel>
  <CollapsePanel className="collapse-code-panel" header="CreateStatsOptionsContext.ts" key="CreateStatsOptionsContext">
```ts
type CreateStatsOptionsContext = {
  forToString?: boolean;
  [key: string]: any;
};
```
  </CollapsePanel>
</Collapse>

## `statsFactory`

<Badge text="只读" type="info" />

此钩子提供了对 `StatsFactory` 的访问，以调用其钩子。该类用于构造 Stats 对象。

- **类型：** `SyncHook<[StatsFactory, StatsOptions]>`
- **参数：**
  - `StatsFactory`：Stats 工厂实例，详见 [Stats Factory 钩子](/api/plugin-api/stats-hooks#statsfactory)
  - `StatsOptions`：Stats 配置

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="StatsFactory.ts" key="StatsFactory">
```ts
type StatsFactory = {
  hooks: StatsFactoryHooks;
  create(
    type: string,
    data: any,
    baseContext: Omit<StatsFactoryContext, 'type'>,
  ): void;
};
```
  </CollapsePanel>
  <CollapsePanel header="StatsOptions.ts" key="StatsOptions">
详见 [Stats 配置](/config/stats)
  </CollapsePanel>
</Collapse>

## `statsPrinter`

<Badge text="只读" type="info" />

此钩子提供了对 `StatsPrinter` 的访问，以调用其钩子。该类用于打印 Stats 信息。

- **类型：** `SyncHook<[StatsPrinter, StatsOptions]>`
- **参数：**
  - `StatsPrinter`：Stats 打印实例，详见 [Stats Printer 钩子](/api/plugin-api/stats-hooks#statsprinter)
  - `StatsOptions`：Stats 配置

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="StatsPrinter.ts" key="StatsPrinter">
```ts
type StatsPrinter = {
  hooks: StatsPrinterHooks;
  print(
    type: string,
    object: {
      [key: string]: any;
    },
    baseContext?: {
      [key: string]: any;
    },
  ): string;
};
```
  </CollapsePanel>
  <CollapsePanel header="StatsOptions.ts" key="StatsOptions">
详见 [Stats 配置](/config/stats)
  </CollapsePanel>
</Collapse>
